package com.android.filter;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;

import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteException;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Path;
import android.media.ExifInterface;
import android.net.Uri;
import android.provider.MediaStore;
import android.renderscript.Allocation;
import android.renderscript.Element;
import android.renderscript.RenderScript;
import android.renderscript.Type;
import android.util.AttributeSet;
import android.util.FloatMath;
import android.util.Log;
import android.widget.ImageView;

import com.android.filter.jni.EffectJNI;
import com.android.filter.jni.VertexInfo;
import com.example.plasma.R;

public class MosaicView extends ImageView {

	private Bitmap mInputBitmap, mOutputBitmap;
	
	private long mStartTime, mEndTime;
	private Context mContext = null;
	public MosaicView(final Context context) {
		super(context);
		mContext = context;
		mPathPaint = new Paint();
		mPathPaint.setStyle(Style.STROKE);
		mPathPaint.setARGB(90, 0, 0, 0);
	}
	public MosaicView(final Context context,  AttributeSet attrs) {
		super(context, attrs);
		mContext = context;
		mPathPaint = new Paint();
		mPathPaint.setStyle(Style.STROKE);
		mPathPaint.setARGB(90, 0, 0, 0);
	}
	public MosaicView(final Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		mContext = context;
		mPathPaint = new Paint();
		mPathPaint.setStyle(Style.STROKE);
		mPathPaint.setARGB(90, 0, 0, 0);
	}

	public void initView(Uri uri)
	{
		if(uri != null)
		{
			mStartTime = System.currentTimeMillis();
			initFileNameNPath(uri, mContext.getContentResolver());
			mInputBitmap = decoding(uri);
			mOutputBitmap = Bitmap.createBitmap(mInputBitmap.getWidth(), mInputBitmap.getHeight(), Config.ARGB_8888);
			byte[] mGrad = new byte[mInputBitmap.getWidth() * mInputBitmap.getHeight()];
			byte[] vGrad = new byte[mInputBitmap.getWidth() * mInputBitmap.getHeight()];
			byte[] hGrad = new byte[mInputBitmap.getWidth() * mInputBitmap.getHeight()];
			
			Log.i("test", "image size:"+mInputBitmap.getWidth() +","+ mInputBitmap.getHeight());
			setScaleType(ScaleType.FIT_CENTER);

			//start render script
			RenderScript renderScript = RenderScript.create(mContext);
			ScriptC_greyscale greyscale = new ScriptC_greyscale(renderScript, getResources(), R.raw.greyscale);
			Allocation inputAllocation = Allocation.createFromBitmap(renderScript, mInputBitmap,
					Allocation.MipmapControl.MIPMAP_NONE,
					Allocation.USAGE_SCRIPT);
			Type t = new Type.Builder(renderScript, Element.U8(renderScript)).setX(mInputBitmap.getWidth()).setY(mInputBitmap.getHeight()).create();
			Allocation greyAllocation = Allocation.createTyped(renderScript, t);
			greyscale.forEach_root(inputAllocation, greyAllocation);
			inputAllocation.destroy();
			inputAllocation = null; 
			//Allocation blur = Allocation.createTyped(renderScript, t);
			Allocation horizontal = Allocation.createTyped(renderScript, t);
			Allocation vertical = Allocation.createTyped(renderScript, t);
			Allocation gradient = Allocation.createTyped(renderScript, t);
			Allocation tempAllocation = Allocation.createTyped(renderScript, t);

			greyscale.set_width(mInputBitmap.getWidth());
			greyscale.set_height(mInputBitmap.getHeight());

			int[] curve = { 154,  255, 154};
			int[] curve_sum = { 0,  154, 409};
			int[] curve_d = {  -154, 0, 154};
			int[] curve_d_sum = { 0, -154, -154};
			//horizontal
			greyscale.set_gIn(greyAllocation);
			greyscale.set_curve_length(1);
			greyscale.set_gaussian_curve_d(false);
			greyscale.set_curve_array(curve);
			greyscale.set_curve_sum_array(curve_sum);
			greyscale.forEach_gaussianDerivVerticalPass(greyAllocation, tempAllocation);
			//	        tempAllocation.copyTo(mGrad);
			greyscale.set_gIn(tempAllocation);
			greyscale.set_gaussian_curve_d(true);
			greyscale.set_curve_array(curve_d);
			greyscale.set_curve_sum_array(curve_d_sum);
			greyscale.forEach_gaussianDerivHorizontalPass(tempAllocation, horizontal);

			//vertical
			greyscale.set_gIn(greyAllocation);
			greyscale.set_gaussian_curve_d(true);
			greyscale.set_curve_array(curve_d);
			greyscale.set_curve_sum_array(curve_d_sum);
			greyscale.forEach_gaussianDerivVerticalPass(greyAllocation, tempAllocation);

			greyAllocation.destroy();  
			greyAllocation = null;

			greyscale.set_gIn(tempAllocation);
			greyscale.set_gaussian_curve_d(false);
			greyscale.set_curve_array(curve);
			greyscale.set_curve_sum_array(curve_sum);
			greyscale.forEach_gaussianDerivHorizontalPass(tempAllocation, vertical);
			tempAllocation.destroy();
			tempAllocation = null;

			greyscale.set_gIn(horizontal);
			greyscale.set_gTmp(vertical);
			greyscale.forEach_gaussianDeriv(horizontal, gradient);
			gradient.copyTo(mGrad);
			gradient.destroy();
			gradient = null;
			//end render script
			horizontal.copyTo(hGrad);
			horizontal.destroy();
			horizontal = null;
			vertical.copyTo(vGrad);
			vertical.destroy();
			vertical = null;
			Log.i("test", "nativeSetEdgeBuffer");
			EffectJNI.nativeSetEdgeBuffer(mGrad, vGrad, hGrad, mInputBitmap.getWidth(), mInputBitmap.getHeight());
			Log.i("test", "nativeSetEdgeBuffer222");
			//	        for(Point p : list)
			//	        	Log.i("test", p.toString());
			greyscale.destroy();
			greyscale = null;
			renderScript.destroy();
			renderScript = null;
//			setImageBitmap(mInputBitmap);
			mPreviewImage = new PreviewImage(mContext, mInputBitmap.getWidth(), mInputBitmap.getHeight());
			mPreviewImage.setBitmap(mInputBitmap);
			mPreviewImage.init(mViewWidth, mViewHeight);

			mGrad = null; vGrad = null; hGrad = null;

			Log.d("TEST", "time:"+(System.currentTimeMillis() - mStartTime));
			
		}
	}
	
	public PreviewImage getPreviewImage()
	{
		return mPreviewImage;
	}
	public void setNullPreviewImage()
	{
		mPreviewImage = null;
		invalidate();
	}
	public Bitmap getImageBitmap()
	{
		return mInputBitmap;
	}
	public void setPreviewImage(PreviewImage preview)
	{
		mPreviewImage = preview;
		invalidate();
	}
	public void destroy()
	{
		EffectJNI.nativeFreeMemory();
		Util.recycleBitmap(mInputBitmap);
		Util.recycleBitmap(mOutputBitmap);
		if(mPreviewImage != null)
			mPreviewImage.destroy();
//		if(mGridInfo != null)
//			mGridInfo.destroy();
	}
	@Override
	public void onDraw(Canvas canvas)
	{
		if(mPreviewImage != null)
		{
			mPreviewImage.drawPreview(canvas, null);
		}
	}
	@Override
	public void onLayout(boolean changed, int left, int top, int right, int bottom)
	{
		super.onLayout(changed, left, top, right, bottom);
		if(changed)
		{
			mViewWidth = right - left;
			mViewHeight = bottom - top;
			if(mPreviewImage != null)
				mPreviewImage.init(mViewWidth, mViewHeight);
		}
	}
	public int getDrawBitmapWidth()
	{
		return mInputBitmap.getWidth();
	}
	public int getDrawBitmapHeight()
	{
		return mInputBitmap.getHeight();
	}
	public String getFileName()
	{
		return mFileName;
	}
	public String getFilePath()
	{
		return mFilePath;
	}
	private int mViewWidth, mViewHeight;
	private Paint mPathPaint = new Paint();
	private PreviewImage mPreviewImage = null;
	private String mFileName = null;
	private String mFilePath = null;
	
	private void initFileNameNPath(Uri uri, ContentResolver cr)
	{
		if (uri.getScheme().toString().compareTo("content")==0)
		{      
			Cursor cursor = cr.query(uri, null, null, null, null);
			if (cursor.moveToFirst())
			{
				int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);//Instead of "MediaStore.Images.Media.DATA" can be used "_data"
				Uri filePathUri = Uri.parse(cursor.getString(column_index));
				mFileName = filePathUri.getLastPathSegment().toString();
				mFilePath = filePathUri.getPath();
				mFilePath = mFilePath.substring(0, mFilePath.indexOf(mFileName));
			}
		}
	}
	private int getRotateDegree(ContentResolver cr, Uri uri)
	{
		if(cr != null && uri != null)
		{
			ExifInterface exif;
			int exifOrientation = -1;
			String path;
			if(uri.toString().contains("/mnt/sdcard"))
			{
				path = uri.getPath();
			}
			else
			{
				String[] proj = { MediaStore.Images.Media.DATA /*, MediaStore.Images.Media.MIME_TYPE*/};
				Cursor cursor = null;

				try{
					cursor = cr.query(uri,
							proj, // Which columns to return
							null, // WHERE clause; which rows to return (all rows)
							null, // WHERE clause selection arguments (none)
							null);
				}catch(SQLiteException e){
					e.printStackTrace();
					return -1;
				}
				if(cursor == null)
				{
					path = null;
					return -1;
				}
				int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);		
				cursor.moveToFirst();
				if(cursor.getCount() == 0)
				{
					if(cursor != null)
						cursor.close();
					cursor = null;
					return -1;
				}

				if(cursor.isNull(column_index))
				{
					if(cursor != null)
						cursor.close();
					cursor = null;

					String temp = "Unknown Jpeg"; 
					path = temp;
				}
				else
				{
					cursor.moveToFirst();
					String ret = cursor.getString(column_index);
					if(cursor != null)
						cursor.close();
					cursor = null;
					path = ret;
				}				
			}
			if(path == null)
				return -1;
			try {
				exif = new ExifInterface(path);
				if(exif != null){
					exifOrientation = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION,ExifInterface.ORIENTATION_NORMAL);
					if(exifOrientation == ExifInterface.ORIENTATION_ROTATE_90)
					{
						return 90;
					}
					else if(exifOrientation == ExifInterface.ORIENTATION_ROTATE_180)
					{
						return 180;
					}
					else if(exifOrientation == ExifInterface.ORIENTATION_ROTATE_270)
					{
						return 270;
					}
					return 0;
				}
			} catch (IOException e) {
				e.printStackTrace();
				return -1;
			}
		}
		return -1;
	}
	
	private Bitmap decoding(Uri uri)
	{
		float scale = 1.f;
		Bitmap bitmap = null;
		Matrix matrix = new Matrix();
		BitmapFactory.Options option = new BitmapFactory.Options();
		option.inJustDecodeBounds = true;

		InputStream is = null;
		try {
			is = mContext.getContentResolver().openInputStream(uri);
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} 

		BitmapFactory.decodeStream(is, null, option);
		int imageSize = option.outWidth * option.outHeight;
		int rotate = getRotateDegree(mContext.getContentResolver(), uri);
		if(rotate == 0){
			matrix.postRotate(0);
		}
		else if(rotate == 90){
			matrix.postRotate(90);
		}
		else if (rotate == 180){
			matrix.postRotate(180);
		}
		else if (rotate == 270){
			matrix.postRotate(270);	    	    				
		}
		else{
			rotate = 0;
		}

		int sampleSize = getSampleSize(FloatMath.sqrt(imageSize), FloatMath.sqrt(MAX_IMAGE_SIZE));
		option.inJustDecodeBounds = false;
		option.inSampleSize = sampleSize;
		option.inPreferredConfig = Config.ARGB_8888;
		option.inMutable = true;

		try {
			is = mContext.getContentResolver().openInputStream(uri);
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} 

		bitmap = BitmapFactory.decodeStream(is, null, option);
		imageSize = bitmap.getWidth() * bitmap.getHeight();

		scale = FloatMath.sqrt(MAX_IMAGE_SIZE / (float)(imageSize));
		if(scale > 0.999f && scale < 1.f)
			scale = 0.999f;
		matrix.postScale(scale, scale);
		Bitmap ret = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);
		Util.recycleBitmap(bitmap);
		return ret;
	}
	
	private int getSampleSize(float bitmapSize, float maxSize)
	{
		int ret = 1;
		for(int i = 1; (bitmapSize / i) > maxSize ; i*=2)
		{
			ret = i;
		}

		return ret;
	}
	private final int MAX_IMAGE_SIZE = 2000000;
}
